package com.foxr.display.components
{
	//import external files
	import com.foxr.controller.Application;
	import com.foxr.data.GlobalConstants;
	import com.foxr.display.*;
	import com.foxr.event.ElementEvent;
	import com.foxr.event.FormEvent;
	
	import flash.net.*;
	import flash.events.*;

	/**
	 * A container class built to load and contain form controls for data entry, 
	 * collection and submission to the server. Form itself will not usually have
	 * a visual representation in the movie, but if it should, it extends CompoundElement
	 * to allow access to border and backgrounds.
	 * 
	 * @langversion 	ActionScript 3.0
	 * @playerversion 	Flash 9
	 * @author			Jeff Fox
	 *
	 * Copyright (c) 2009 Jeff Fox. Licensed under the MIT License.
	 *
	 *
	 */
	 public class Form extends CompoundElement {
		/*--------------------------------------
		/	VARIABLES
		/-------------------------------------*/
		public static const VALIDATE_DATE:String = "date";
		public static const VALIDATE_PHONE_US:String = "usPhone";
		public static const VALIDATE_PHONE_GENERIC:String = "genPhone";
		public static const VALIDATE_EMAIL:String = "email";
		public static const VALIDATE_WORD:String = "word";
		public static const VALIDATE_NUMBER:String = "number";
		public static const VALIDATE_FILENAME:String = "filename";
		public static const VALIDATE_LONG_FILENAME:String = "longFilename";
		public static const VALIDATE_URL:String = "url";
		public static const VALIDATE_DOLLARS:String = "usdollars";
		public static const VALIDATE_ZIPCODE_US:String = "zipcode";
		public static const VALIDATE_SOCIALSECURITY:String = "socSecurity";
		public static const VALIDATE_NAME:String = "name";
		public static const VALIDATE_DOMAIN:String = "domain";
		public static const VALIDATE_ALPHANUMERIC:String = "alphanumeric";	
		
		public static const FORM_SUBMIT:String = "formsubmit";	
		/**
		 * URLRequest object
		 * @var	_vars:URLRequest
		 */
		private var _vars:URLRequest = null;
		/**
		 * Protocol
		 * @var	_protocol:String
		 */
		private var _protocol:String = '';
		/**
		 * Domain
		 * @var	_domain:String
		 */
		private var _domain:String = '';
		/**
		 * Action
		 * @var	_action:String
		 */
		private var _action:String = '';
		/**
		 * Method
		 * @var	_method:String
		 */
		private var _method:String = 'GET';
		/**
		 * Method
		 * @var	_method:String
		 */
		private var _radioGroups:Array = null;
		/**
		 * Validate.
		 * @var	_validate:Boolean
		 */
		private var _validate:Boolean = false;
		/**
		 * Fields.
		 * @var	_fields:Array
		 */
		private var _fields:Array = null;
		/**
		 * Validation Log.
		 * @var	_validationLog:Array
		 */
		private var _validationLog:Array = null;
		/**
		 * Verbose.
		 * @var	_verbose:Boolean
		 */
		private var _verbose:Boolean = true;
		/**
		 * Validation Error Message.
		 * @var	_validationErrorMess:String
		 */
		private var _validationErrorMess:String = '';
		/**
		 * Values.
		 * @var	_values:Object
		 */
		private var _values:Object = null;
		/*--------------------------------------
		/	CONSTRUCTOR
		/-------------------------------------*/
		/**
		 * Construct a new Form instance
		 *
		 */
		public function Form() {
			super();
			_vars = new URLRequest();
			_radioGroups = [];
			_fields = [];
			_validationLog = [];
			addEventListener(ElementEvent.ELEMENT_ADDED, addItem);
		}
		/*--------------------------------------
		/	SET/GET FUNCTIONS
		/-------------------------------------*/
		/**
		 * The submission URL. 
		 * @since	1.0
		 * @param	a	The Valid action URL or ActionScript function name
		 *
		 */
		public function get action():String { return _action; }
		public function set action(a:String):void { _action = a; }
		/**
		 * Assigns a valid domain to the submission string. Leave blank when specifying 
		 * an absolute submission URL using <i>action</i>.
		 * @since	1.0
		 * @param	d	Domain name
		 *
		 */
		public function get domain():String { return _domain; }
		public function set domain(d:String):void { _domain = d; }
		/**
		 * The submission method. GET is recommended for most submissions.
		 * @since	1.0
		 * @param	m	The submission method (GET or POST)
		 *
		 */
		public function get method():String { return _method; }
		public function set method(m:String):void { _method = m; }
		/**
		 * Assigns the http protocol (http or https) to the beginning of the 
		 * submission string. Leave blank when specifying 
		 * an absolute submission URL using <i>action</i>
		 * @usage
		 * <b>Example:</b><br />
		 * var form:Form = Form (addElement('form',Form));
		 * @since	1.0
		 * @param	p	Valid http protocol
		 *
		 */
		public function get protocol():String { return _protocol; }
		public function set protocol(p:String):void { _protocol = p; }
		/**
		 * Use this to retrieve an error message generated by the object during validation.
		 * @since	1.0
		 * @return		The validation error message
		 *
		 */
		public function get validationErrorMessage():String { return _validationLog.join(); }
		/**
		 * Applies whether the form object should perform field validation when submitting. To 
		 * be completed, the form needs at least one child object assigned vis the <i>addItem</i> method.
		 * @since	1.0
		 * @param	v	TRUE or FALSE
		 * @return		The validateOnSubmit value
		 *
		 */
		public function get validateOnSubmit():Boolean { return _validate; }
		public function set validateOnSubmit(v:Boolean):void { _validate = v; }
		/**
		 * Applies whether the object should output validation error to the trace handler.
		 * @since	1.0
		 * @param	v	TRUE or FALSE
		 * @return		The verbose value
		 *
		 */
		public function get verbose():Boolean { return _verbose; }
		public function set verbose(v:Boolean):void { _verbose = v; }
		/**
		 * Assigns the passed variable to the internal variable storage object. The object 
		 * should have be passed using the <i> URLVariables.String()</i>  method.
		 * @usage
		 * <b>Example</b>:<br />
		 * parentObj = Form (parentObj);
		 * var vars:URLVariables = new URLVariables(this.variable+'='+txt.text);
		 * parentObj.variable = vars.toString();
		 * @since	1.0
		 * @param	v	A URLVariables.toString() object. 
		 *
		 */
		public function set variable(v:Object):void { _vars.data = v; }
		/**
		 * Returns an object containing the loaded varables and values 
		 * @since	1.0
		 * @param	m	A string representation of the loaded variables
		 *
		 */
		public function get variables():Object { return _vars.data.toString(); }
		/**
		 * An array containing collected and vaidated values for retrieval by parent objects during
		 * Flash method submissions.
		 * @since	1.0
		 * @param	m	Object of field values.
		 *
		 */
		public function get values():Object { return _values; }
		/*--------------------------------------
		/	PUBLIC FUNCTIONS
		/-------------------------------------*/
		/**
		 * Adds a control object to the form for use in performing validation on submit
		 * @since	1.0
		 * @param	name	The name of the object
		 * @param	type	The objects Class name
		 * @param	required	TRUE or FALSE
		 * @param	format		(Optional) A validation format for the items value. Valid formats include:<p />
		 * <ul>
		 *     <li>date</li>
		 *     <li>usPhone - Use for US phone numbers</li>
		 *     <li>genPhone - use for international numbers</li>
		 *     <li>email - Email adress</li>
		 *     <li>word</li>
		 *     <li>number</li>
		 *     <li>filename</li>
		 *     <li>url</li>
		 *     <li>usdollars</li>
		 *     <li>zipcode</li>
		 *     <li>socSecurity</li>
		 *     <li>name</li>
		 *     <li>domai</li>
		 *     <li>alphanumeric</li>
		 * </ul>
		 *
		 */
		public function addItem(name:String='', type:Class = null,required:Boolean = false,format:String= '',errorAlert:String = ''):void {
			
			if (name != '' && type != null) {
				_fields.push( { name:name, type:type, required:required, format:format, errorAlert:errorAlert } );
			} else {
				// Auto add function
				try {
					var tmpComp:Component = Component(getChildAt(numChildren -1));
					gpm.log.debug("Component " + tmpComp.name + " added as child");
					if (tmpComp.name != '' && tmpComp.className != null) {
						_fields.push( { name:tmpComp.name, type:tmpComp.className, required:tmpComp.required, format:tmpComp.pattern, errorAlert:errorAlert } );
					}
					gpm.log.debug("Component " + tmpComp.name + " added to field list.");
				} catch (e:Error) {
					// Element added is not a component, so quietly handle the error and continue
					//trace("Element " + Element(getChildAt(numChildren - 1)).name + " is not a component. Error : " + e);
				}
			}
		}
		/**
		 * Adds a radio group to the object.
		 * @since	1.0
		 * @param	group	A Radio Group Object
		 *
		 */
		public function addRadioGroup(group:RadioGroup):void { _radioGroups[group.name] = group;  }
		/**
		 *	If an action has been defined, submits the variables contained in the _submitter object.
		 *  @since 1.0
		 * 	@param	e	Event
		 */
		public function submit(e:Event):Boolean {
			var isSubmitted:Boolean = false;
			//var dlg:Dialog = null;
			if (_action != '') {
				var proceed:Boolean = true;
				if (_validate == true) proceed = validate();
				if (proceed) {
					_values = new Object();
					for (var j:Number = 0; j < _fields.length; j++) {
						_values[Component(getChildByName(_fields[j].name)).variable] = Component(getChildByName(_fields[j].name)).value;
					} // END for
						
					// Decide if this form should submit the loaded vars as a standard URL POST/GET reuqest (requiring
					// a URLVariables object) or an internal Flash function for AMF/Remoting
					if (_action.search("asfunction:") != -1) {
						var tmpAction:String = _action.substring(11, _action.length);
						// Flash Function
						parent[tmpAction](e);
					} else {
						// AUTOMATIC HTTP POST/GET Submission
						// TEST if there is a radioGroup
						if (_radioGroups.length > 0) {
							for (var i:Number = 0; i < _radioGroups.length; i++) {
								var vars:URLVariables = new URLVariables(_radioGroups[i].variable+'='+_radioGroups[i].value);
								_vars.data = vars.toString();
							} // End for
						} // END if
						_vars.method = _method;
						var url:String = '';
						if (!_action.substring(0, 4) == 'http') {
							url += _protocol;
							if (_domain != '')  url += _domain;
						} // END if
						url += _action;
						_vars.url = url;
						var post:URLLoader = new URLLoader();
						post.addEventListener(Event.COMPLETE,onSubmit);
						post.load(_vars);
					} // END if
					isSubmitted = true;
				} else {
					var outStr:String = gpm.copy.getCopyString(GlobalConstants.COPY_FORM_VALIDATION_MESSAGE);
					if (_verbose) {
						outStr += "<br>";
						for (var k:Number = 0; k < _validationLog.length; k++) {
							//trace(_validationLog[k]); // END for
							outStr += _validationLog[k] + "<br>";
						} // END for
						gpm.log.error("Form, Submit validation problems were found." + outStr);
					} // END if
					//dlg = Dialog(Application.getInstance().showMessageDialog('dlg', Dialog, { titleBarText:"Form Validation Problems Found", 
					sendNotification(Application.SHOW_MESSAGE,{ name:'dlgValidation',className:Dialog,props:{titleBarText:"Form Validation Problems Found", 
					text:outStr } } );
					//sendNotification(FormEvent.VALIDATION_FAIL);
				} // END if
			} else {
				gpm.log.error("Form submission error. No ACTION was specified.");
			}// END if
			return isSubmitted;
		}
		/*--------------------------------------
		/	PRIVATE FUNCTIONS
		/-------------------------------------*/
		/**
		 *	Validates any control objects added to the object. tests for required fields and 
		 *  also tests against any value formats passed
		 *  @since 1.3
		 */
		private function validate():Boolean {
			// SET (or reset) THE VALIDATION LOG and TEST VAR
			_validationLog = [];
			var test:Boolean = true;
			for (var i:Number = 0; i < _fields.length; i++) {
				// Create a temp object of the item
				var tmpItem:* = _fields[i].type(this.getChildByName(_fields[i].name));
				if (tmpItem != null) {
					// REQUIRED TEXT CHECK
					if (_fields[i].required == true && (tmpItem.value == null || tmpItem.value == '')) {
						// TEST
						test = false;
						_validationLog.push(tmpItem.displayName + " field is missing a required value.");
						break;
					} // END if
					if (test) {
						if (_fields[i].format != '') {
							var rEx:RegExp = getRegExData(_fields[i].format,1);
							if (!(test = rEx.test(tmpItem.value))) {
								_validationLog.push(tmpItem.displayName + " value is not in the proper " + _fields[i].format + " format.");
								_validationErrorMess = getRegExData(_fields[i].format,0);
								test = false;
								break;
							} // END if
						} // END if
					} // END if
					// IF testing failed for any reson, set focus to the current field
					if (!test) {
						stage.focus = tmpItem;
						if (tmpItem is TextInput) {
							TextInput(tmpItem).setFocus();
						} // END if
					} // END if
				} else {
					gpm.log.error("ERROR: Field " + _fields[i].displayName + " was not found as a child object of " + this.name);
				} // END if
			} // END for
			return test;
		}
		/**
		 *	Handles the submission response.
		 *  @since 1.0
		 * 	@param	e	Event
		 */
		private function onSubmit(e:Event):void {
			gpm.log.debug("Form submitted");
			if (_verbose) {
				var valueStr:String = "Form values = <br>";
				for (var val:String in _values) {
					valueStr += val + " = " + _values[val] + "<br>";
				}
			}
			sendNotification(FormEvent.VALIDATION_SUBMIT_SUCCEED,{values:valueStr});
		}
		/**
		 *	Accepts format type and return type arguments and returns either a RegExp pattern
		 *  for testing or an associated error message.
		 *  @since 1.3
		 */
		private function getRegExData(type:String,rType:Number):* {
			var pattern:RegExp = new RegExp("\w");
			var errMess:String = '';
			switch (type) {
				// SHORT DATE
				case VALIDATE_DATE:
					pattern = /^\d?\d\/\d?\d\/[0-9]{4}$/;
					errMess = "Please enter all dates in MM/DD/YYYY format.";
					break;
				// US PHONE NUMBER
				case VALIDATE_PHONE_US:
					pattern = /^\([0-9]{3}\)\s?[0-9]{3}\-[0-9]{4}$/;
					errMess = "US Phone Numbers must be contain an area code and 7 digits. (e.g., (123) 345-6789).";
					break;
				// GENERAL PHONE NUMBER - ACCEPTS US AND INTERNATIONAL PHONE NUMBERS
				case VALIDATE_PHONE_GENERIC: // ADDED 5/21/02
					pattern = /^[0-9\-\(\)\+\s]+$/;
					errMess = "Valid Phone Numbers can only include numbers, hyphens(-), parenthese () or spaces.";
					break;	
				// E-MAIL ADDRESS
				case VALIDATE_EMAIL:
					pattern = /^[a-z_0-9\.\-\_]+@[a-z_0-9\.\-\_]+\.[a-z_0-9\.]*[a-z]{2,3}$/;
					errMess = "Please enter the e-mail address in recipient@hostname.com format.";
					break;
				// WORDS WITH SPACES
				case VALIDATE_WORD:
					pattern = /^[a-z\s]+$/i;
					errMess = "Please enter only words with spaces and without punctuation or numbers.";
					break;
				// NUMBER WITH COMMAS
				case VALIDATE_NUMBER :
					pattern = /^[0-9\,]+$/;
					errMess = "Please enter only a number values without letters or punctuation.";
					break;
				//	EIGHT dot THREE FILE NAME
				case VALIDATE_FILENAME:
					pattern = /^\w{1,8}\.[a-z]{1,3}$/;
					errMess = "The name of this file must conform to the 8.3. naming convention with one to eight letters as the file name and one to three letters for a file extension. (e.g., filename.ext).";
					break;
				//	NORMAL FILE NAME
				case VALIDATE_LONG_FILENAME:
					pattern = /^(.*?)\.[a-zA-Z]{1,4}$/;
					errMess = "The name of this file must conform to standard file naming convention with one to four letters for a file extension. (e.g., filename.ext).";
					break;
				// INTERNET URL
				case VALIDATE_URL:		// NEW 2/28/02
					pattern = /^https?\:\/\/[a-z_0-9\_\-]+\.[a-z_0-9\_\-]+\.?[a-z_0-9\.\_\-\/\?\&\%\=]+$/i;
					errMess = "Web site URLs must contain the http:// or https:// prefix";
					break;
				// US DOLLARS
				case VALIDATE_DOLLARS:	// NEW 2/28/02
					pattern = /^\$?[0-9\,]+\.[0-9]{2}$/i;
					errMess = "The dollar amount specified is not in the correct format. Enter dollar values as $###,###.## format.";
					break;
				// US POSTAL CODE
				case VALIDATE_ZIPCODE_US:	// NEW 3/1/02
					pattern = /^[0-9]{5}\-?([0-9]{4})?$/i;
					errMess = "US Zip Codes must be entered in either 12345 or 12345-6789 format.";
					break;
				// US SOCIAL SECURITY NUMBER
				case VALIDATE_SOCIALSECURITY :	// NEW 4/5/02
					pattern = /^[0-9]{3}\-[0-9]{2}\-[0-9]{4}$/;
					errMess = "Social Security Numbers must be entered in 123-45-6789 format.";
					break;
				// NAME - CAN CONTAIN -,. CHARS AS WELL AS LETTERS
				case VALIDATE_NAME:	// NEW 5/21/02
					pattern = /^[a-zA-Z\s\.\-\,]+$/i;
					errMess = "Names must be made up of letters wihout mubers and can contain only hyphns(-), commas (,) or periods(.).";
					break;	
				case VALIDATE_DOMAIN:	// NWS 8/30/02
					pattern = /^[a-z_0-9\-\_]{3,}\.[a-z_0-9\-\_]+\.[a-zA-Z]{2,4}(\.[\w\-]{2,})?$/;
					errMess = "You must specify domain names in the format: www.domainname.ext or subdomain.domain.co.uk";
					break;
				case VALIDATE_ALPHANUMERIC: // ALPHA NUMBERIC 1/22/03
					pattern = /^[a-z_0-9]*$/;
					errMess = "You can only enter alphanumeric characters without punctuation or special characters in this field.";
					break;
				default:
					break;
					return false;
			} // END switch
			if (rType == 1)
				return pattern;
			else
				return errMess; // END if
		}
	}
}